package gnss

import (
	"dgo/goutils/utils"
	"fmt"
	"math"
	"time"
)

type YObsRinex struct {
	decodestate   int8
	rinexHeader   *RinexHeaderEx
	StartTime_OBS time.Time
	cachelist     *utils.ArrayList
	FixInterval   int64 // 固定采样间隔 单位：秒  5s,  mod(t, 5) = 0
	HeadCacheNum  int
	RinexOutputFn func(yobs *GnssYObsEpoch, str string)
	OnGetTimeFunc func(yobs *GnssYObsEpoch) time.Time
}

func NewYObsRinex() *YObsRinex {
	rval := &YObsRinex{
		decodestate:  -1,
		HeadCacheNum: 30,
	}
	return rval
}

func (this *YObsRinex) GetNow(yobs *GnssYObsEpoch) time.Time {
	if this.OnGetTimeFunc != nil {
		return this.OnGetTimeFunc(yobs)
	}
	return time.Now()
}

func (this *YObsRinex) checkInterval(yobs *GnssYObsEpoch) bool {
	if this.FixInterval > 0 {
		f_msec := float64(yobs.TOW)
		i_msec := int64(math.Floor(f_msec + 0.1))
		v := i_msec % (this.FixInterval * 1000)
		if v != 0 {
			return false
		} else {
			return true
		}
	}
	return true
}

func (this *YObsRinex) GetRinexHeader() string {
	// first line
	str := PrintRinexFV(3.04, 2, 9) + FPrintRinexStrV("OBSERVATION DATA", 27) + FPrintRinexStrV("M (MIXED)", 13)
	str = utils.AddSuffixForWidth(str, 60, " ")
	str += "RINEX VERSION / TYPE\n"

	str += utils.AddSuffixForWidth(RINEX_TransVer, 20, " ") +
		utils.AddSuffixForWidth(RINEX_CopyRight, 20, " ") +
		utils.AddSuffixForWidth(this.StartTime_OBS.Format("20060102 150405")+" UTC", 20, " ") +
		"PGM / RUN BY / DATE\n"

	str += this.rinexHeader.PrintSignHeader()
	if !this.StartTime_OBS.IsZero() {
		str += "  " + this.StartTime_OBS.Format("2006    01    02    15    04   05") + ".0000000     GPS         TIME OF FIRST OBS\n"
	}
	str += utils.AddSuffixForWidth("", 60, " ") + "END OF HEADER\n"
	return str
}

//func (this *YObsRinex) innerOutputHeader() {
//	this.innerOutputRinex(this.GetRinexHeader())
//}

func (this *YObsRinex) innerOutputRinex(yobs *GnssYObsEpoch, data string) {
	if this.RinexOutputFn != nil {
		this.RinexOutputFn(yobs, data)
	}
}

func (this *YObsRinex) innerProcessObs(yobs *GnssYObsEpoch) {
	var sb utils.BytesBuilder
	n := 0
	yobs.RangeSys(SYS_BD, func(obs *GnssYObs) bool {
		n++
		sb.AppendStr(GetPrnStrCode(obs.Satid, obs.NaviSys)).AppendStr(" ")
		for i := 0; i < len(this.rinexHeader.CHeader); i++ {
			sig := obs.GetSingal(this.rinexHeader.CHeader[i])
			if sig != nil {
				sb.AppendStr(PrintRinexFV(sig.PsV, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(sig.PhV, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(sig.Doppler, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(float64(sig.Cnr), 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI
			} else {
				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI
			}
		}
		sb.AppendStr("\n")
		return true
	})

	yobs.RangeSys(SYS_GPS, func(obs *GnssYObs) bool {
		n++
		sb.AppendStr(GetPrnStrCode(obs.Satid, obs.NaviSys)).AppendStr(" ")
		for i := 0; i < len(this.rinexHeader.GHeader); i++ {
			sig := obs.GetSingal(this.rinexHeader.GHeader[i])
			if sig != nil {
				sb.AppendStr(PrintRinexFV(sig.PsV, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(sig.PhV, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(sig.Doppler, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(float64(sig.Cnr), 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI
			} else {
				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI
			}
		}
		sb.AppendStr("\n")
		return true
	})

	yobs.RangeSys(SYS_GLO, func(obs *GnssYObs) bool {
		n++
		sb.AppendStr(GetPrnStrCode(obs.Satid, obs.NaviSys)).AppendStr(" ")
		for i := 0; i < len(this.rinexHeader.GloHeader); i++ {
			sig := obs.GetSingal(this.rinexHeader.GloHeader[i])
			if sig != nil {
				sb.AppendStr(PrintRinexFV(sig.PsV, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(sig.PhV, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(sig.Doppler, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(float64(sig.Cnr), 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI
			} else {
				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI
			}
		}
		sb.AppendStr("\n")
		return true
	})

	yobs.RangeSys(SYS_GAL, func(obs *GnssYObs) bool {
		n++
		sb.AppendStr(GetPrnStrCode(obs.Satid, obs.NaviSys)).AppendStr(" ")
		for i := 0; i < len(this.rinexHeader.EHeader); i++ {
			sig := obs.GetSingal(this.rinexHeader.EHeader[i])
			if sig != nil {
				sb.AppendStr(PrintRinexFV(sig.PsV, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(sig.PhV, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(sig.Doppler, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(float64(sig.Cnr), 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI
			} else {
				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI
			}
		}
		sb.AppendStr("\n")
		return true
	})

	yobs.RangeSys(SYS_QZS, func(obs *GnssYObs) bool {
		n++
		sb.AppendStr(GetPrnStrCode(obs.Satid, obs.NaviSys)).AppendStr(" ")
		for i := 0; i < len(this.rinexHeader.JHeader); i++ {
			sig := obs.GetSingal(this.rinexHeader.JHeader[i])
			if sig != nil {
				sb.AppendStr(PrintRinexFV(sig.PsV, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(sig.PhV, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(sig.Doppler, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(float64(sig.Cnr), 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI
			} else {
				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI

				sb.AppendStr(PrintRinexFV(0, 3, 13)).AppendStr(" ")
				sb.AppendStr("  ") //LLI SSI
			}
		}
		sb.AppendStr("\n")
		return true
	})
	if n > 0 {
		var header utils.BytesBuilder
		header.AppendStrs("> ", yobs.GetGpsTime(this.GetNow(yobs)).Format("2006 01 02 15 04 05.0000000")+"  0 "+fmt.Sprintf("%.2d", n)).AppendStr("\n")
		this.innerOutputRinex(yobs, header.String())
		this.innerOutputRinex(yobs, sb.String())
	}
}

func (this *YObsRinex) ProcessCache() {
	if this.cachelist == nil {
		return
	}
	this.cachelist.Range(func(val interface{}) bool {
		this.innerProcessObs(val.(*GnssYObsEpoch))
		return true
	})
	this.cachelist = utils.NewArrayList(24)
}

func (this *YObsRinex) InputYObs(yobs *GnssYObsEpoch) {
	if this.decodestate <= 0 {
		this.cachelist = utils.NewArrayList(24)
		this.rinexHeader = NewRinexHeaderEx()
		this.decodestate = 1
	}

	if this.decodestate == 1 {
		yobs.Range(func(navisys byte, yobs *GnssYObs) bool {
			yobs.RangeSignal(func(signal *YObsSignal) bool {
				this.rinexHeader.CheckAddSign(signal.NaviSys, YObsSigID2Code(signal.NaviSys, signal.SigId))
				return true
			})
			return true
		})
		if !this.checkInterval(yobs) {
			return
		}
		this.cachelist.Append(yobs)

		if this.cachelist.Count() > this.HeadCacheNum {
			// this.innerOutputHeader()
			this.cachelist.Range(func(val interface{}) bool {
				this.innerProcessObs(val.(*GnssYObsEpoch))
				return true
			})
			// this.cachelist = utils.NewArrayList(10)
			this.decodestate = 2
		}
	} else {
		if !this.checkInterval(yobs) {
			return
		}
		this.innerProcessObs(yobs)
	}
}
